package com.baturu.vin.hyundai.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baturu.vin.base.exception.EPCDataNotStandardException;
import com.baturu.vin.base.pool.EmailPool;
import com.baturu.vin.base.service.EPCCacheRealTimeProcess;
import com.baturu.vin.base.service.EPCVinWaitCacheService;
import com.baturu.vin.dto.EPCAnalyserEnum;
import com.baturu.vin.hyundai.constant.HyundaiKiaSource;
import com.baturu.vin.hyundai.constant.HyundaiKiaVariables;
import com.baturu.vin.hyundai.dal.dao.HyundaiKiaCommonDAO;
import com.baturu.vin.hyundai.dal.dao.HyundaiKiaVehicleDAO;
import com.baturu.vin.hyundai.dto.FeatureInfoDTO;
import com.baturu.vin.hyundai.dto.HyundaiAttributeEntity;
import com.baturu.vin.hyundai.dto.HyundaiKiaVehicleDTO;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service("hyundaiKiaEPCCacheRealTimeProcess")
public class HyundaiEPCCacheRealTimeProcess implements EPCCacheRealTimeProcess {

    @Autowired
    private HyundaiKiaAttributeService attributeService;

    @Autowired
    private HyundaiKiaFeatureService featureService;

    @Autowired
    private HyundaiKiaCommonDAO commonDAO;

    @Autowired
    private HyundaiKiaVehicleDAO vehicleDAO;

    private EmailPool emailPool = EmailPool.getInstance();

    @Autowired
    private EPCVinWaitCacheService cacheService;

    @Override
    public boolean buildVinAnalyticalResults(String vinCode, String analyticalResultText) throws Exception {
        if (analyticalResultText.contains("Exception")) {
            String log = "现代起亚vin解析错误,vin:" + vinCode + ";爬取结果" + analyticalResultText;
            emailPool.putEmailToQueue(log);
            return false;
        }

        // 根据接口约定接下来的处理将几乎不做异常处理，异常的属于不符合约定的交给上层处理，但其他异常要自己处理
        HyundaiKiaAnalyticalResultDTO analyticalResultDTO = JSON.parseObject(analyticalResultText, HyundaiKiaAnalyticalResultDTO.class);
        if (analyticalResultDTO.getVinCheck()==null||analyticalResultDTO.getVinList()==null) {
            String log = "现代起亚vin解析错误,vin:" + vinCode + ";爬取结果" + analyticalResultText;
            emailPool.putEmailToQueue(log);
            return false;
        }
        String[] vinList = analyticalResultDTO.getVinListForPretreated();
        String[] vinCheck = analyticalResultDTO.getVinCheckForPretreated();
        String[][] vinInfo = analyticalResultDTO.getVinInfoForPretreated();

        // 将数据分类
        VehicleInfoDTO vehicleInfo = getVehicleInfo(vinCode, vinList, vinCheck);
        BasicInfoDTO basicInfo = getBasicInfo(vinInfo[0]);
        MajorAttributeInfoDTO majorAttributeInfo = getMajorAttributeInfo(vinInfo[1]);
        saveCombinationAndMajorAttribute(vehicleInfo, majorAttributeInfo);
        FeatureInfoDTO featureInfo = featureService.getFeatureInfo(vinInfo[2]);
        featureService.checkAndSaveFeatureInfo(featureInfo,"zh");
        FeatureInfoDTO featureInfoEn = featureService.getFeatureInfo(vinInfo[3]);
        featureService.checkAndSaveFeatureInfo(featureInfoEn,"en");
        // 分类数据转换为解析结果
        HyundaiKiaVehicleDTO vehicleDTO = transformVehicleDTO(vehicleInfo, basicInfo, majorAttributeInfo, featureInfo);
        String combination = StringUtils.replace(vehicleDTO.getCombination(), "_", "");
        if (StringUtils.isBlank(combination)) {
            cacheService.reCrawlingVinCode(Sets.newHashSet(vehicleDTO.getVinCode()));
        }
        saveOrUpdateVehicle(vehicleDTO);
        return true;
    }

    /**
     * 保存组合信息和关键信息枚举
     * @param vehicleInfo 基础车型信息
     * @param majorAttributeInfo 关键属性信息
     */
    private void saveCombinationAndMajorAttribute(VehicleInfoDTO vehicleInfo, MajorAttributeInfoDTO majorAttributeInfo) {
        String bpno = vehicleInfo.getBpno();
        List<String> combinations = Lists.newArrayList(majorAttributeInfo.getCombination());
        List<HyundaiAttributeEntity> attributes = majorAttributeInfo.getAttributesClearEmpty();
        attributes = attributes.stream().map(a -> {
            HyundaiAttributeEntity clone = a.clone();
            String describe = clone.getDescribe();
            Map<String, Object> parameter = attributeService.transformMajorToBtrAttribute(describe);
            clone.setRemark(parameter == null ? null : JSON.toJSONString(parameter, SerializerFeature.NotWriteDefaultValue));
            return clone;
        }).filter(a -> StringUtils.isNoneBlank(a.getCode()) && a.getRemark() != null).collect(Collectors.toList());

        commonDAO.saveModelCombination(HyundaiKiaSource.VEHICLE_INFO, "", bpno, combinations);
        if (!attributes.isEmpty()) {
            commonDAO.saveMajorAttribute(HyundaiKiaSource.VEHICLE_INFO, "", bpno, attributes);
        }
    }

    private void saveOrUpdateVehicle(HyundaiKiaVehicleDTO vehicleDTO) {
        String vinCode = vehicleDTO.getVinCode();
        HyundaiKiaVehicleDTO vehicle = vehicleDAO.findVehicle(vinCode);
        if (vehicle == null) {
            vehicleDAO.save(vehicleDTO);
        }else {
            vehicleDAO.update(vehicleDTO);
        }
    }

    private HyundaiKiaVehicleDTO transformVehicleDTO(VehicleInfoDTO vehicleInfo, BasicInfoDTO basicInfo, MajorAttributeInfoDTO attributeInfo, FeatureInfoDTO featureInfo) throws ParseException {
        return HyundaiKiaVehicleDTO.builder()
                // 构造数据版本
                .version(HyundaiKiaVariables.VEHICLE_VERSION)
                // 来自vehicleInfo
                .vinCode(vehicleInfo.getVinCode())
                .makeDate(vehicleInfo.getMakeDate())
                .level(vehicleInfo.getLevel())
                .bpno(vehicleInfo.getBpno())
                .model(vehicleInfo.getModel())
                // 来自basicInfo
                .engineCode(basicInfo.getEngineCode())
                .transmissionCode(basicInfo.getTransmissionCode())
                .startDate(basicInfo.getStartDate())
                .endDate(basicInfo.getEndDate())
                .country(basicInfo.getCountry())
                .interiorCode(basicInfo.getInteriorCode())
                .interiorDesc(basicInfo.getInteriorDesc())
                .exteriorCode(basicInfo.getExteriorCode())
                .exteriorDesc(basicInfo.getExteriorDesc())
                .basicAllDesc(basicInfo.toString())
                .areaNum(basicInfo.getAreaNum())
                .micd(basicInfo.getMicd())
                .ocn(basicInfo.getOcn())
                .ver(basicInfo.getVer())
                .carTypeYear(basicInfo.getCarTypeYear())
                // 来自specialInfo
                .drivePosCode(attributeInfo.getDrivePosCode())
                .sellAreaCode(attributeInfo.getSellAreaCode())
                .combination(attributeInfo.getCombination())
                .majorAttributes(attributeInfo.getAttributeJson())
                // 来自featureInfo
                .featureCodes(featureInfo.getSimpleJson())
                .build();
    }

    /**
     * 从vinList、vinCheck获取vin关联信息
     * @param vinList vinList返回数据
     * @param vinCheck vinCheck返回数据
     * @return vin关联信息
     * @throws EPCDataNotStandardException
     */
    private VehicleInfoDTO getVehicleInfo (String vinCode, String[] vinList, String[] vinCheck) throws EPCDataNotStandardException {
        // 获取基本在DTO，先保留这一层，防止业务变复杂
        return new VehicleInfoDTO(vinCode, vinList, vinCheck);
    }

    @AllArgsConstructor
    @FieldDefaults(level = AccessLevel.PRIVATE)
    private static class VehicleInfoDTO {
        /**
         * vinCode
         */
        @Getter
        String vinCode;
        /**
         * 第一接口第一列
         */
        @Getter
        String[] vinList;
        /**
         * 第二接口第一列
         */
        @Getter
        String[] vinCheck;
        /**
         * 出厂时间
         */
        String getMakeDate() throws ParseException {
            Date date = DateUtils.parseDate(vinList[1], "yyyy/MM/dd");
            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
            return formatter.format(date);
        }
        /**
         * H=现代，K=起亚
         */
        String getLevel() {
            return vinList[4];
        }
        /**
         * 车型编码
         */
        String getBpno() {
            return vinCheck[0];
        }
        /**
         * 车型名称
         */
        String getModel() {
            return vinCheck[1];
        }
    }

    /**
     * 从源数据集中整理车型基本信息，vinInfo[0]中大概为基本车款信息
     * 目前改造逻辑不想加太多字段解析过程中不断发现再添加其他字段处理
     * @param basicArr 基本源数据
     * @return 基本数据信息
     */
    private BasicInfoDTO getBasicInfo (String[] basicArr) throws EPCDataNotStandardException {
        // 做个校验，一旦数据变化了或不符合立刻解决
        if (basicArr == null || (basicArr.length >=44)) {
            log.warn("解析器【" + EPCAnalyserEnum.HYUNDAI + "】解析车型基本描述不符合预期！");
        }
        return new BasicInfoDTO(basicArr);
    }

    @AllArgsConstructor
    @FieldDefaults(level = AccessLevel.PRIVATE)
    private static class BasicInfoDTO {
        /**
         * 其他说明信息
         */
        @Getter
        String[] data;
        /**
         * 引擎编码
         */
        String getEngineCode() {
            return data[11];
        }
        /**
         * 波箱编码
         */
        String getTransmissionCode() {
            return data[12];
        }
        /**
         * 开始时间
         */
        String getStartDate() {
            return data[26];
        }
        /**
         * 结束时间
         */
        String getEndDate() {
            return data[27];
        }
        /**
         * 国家
         */
        String getCountry() {
            return data[36];
        }
        /**
         * 内饰编号
         */
        String getInteriorCode() {
            return data[7];
        }
        /**
         * 内饰描述
         */
        String getInteriorDesc() {
            return data[43];
        }
        /**
         * 外饰描述
         */
        String getExteriorCode() {
            return data[38];
        }
        /**
         * 外饰描述
         */
        String getExteriorDesc() {
            return data[41];
        }
        /**
         * 地区编号
         */
        private String getAreaNum(){ return data[0];}
        /**
         *车型编号
         */
        private String getMicd(){return data[1];}
        /**
         * 版本号
         */
        private String getOcn(){ return data[2];}
        /**
         * 版本子号
         */
        private String getVer(){return data[3];}
        /**
         * 车型年
         */
        private String getCarTypeYear(){return data[4];}
        /**
         * 基本信息描述所有内容，以"|"分隔
         */
        @Override
        public String toString() {
            return JSON.toJSONString(data);
        }
    }

    /**
     * 从源数据集中整理车型特殊信息，vinInfo[1]中大概为车款特殊描述
     * @param attributeArr 特殊源数据
     * @return 特征数据信息
     */
    private MajorAttributeInfoDTO getMajorAttributeInfo (String[] attributeArr) throws EPCDataNotStandardException {
        // 每个数据都是","切割为三段的数据，WT和DT开头的能特殊对应，01-XX开头的基本属于可能有可能没有的描述数据，如果单个作为列会不能对齐
        HyundaiAttributeEntity drivePos = null;
        HyundaiAttributeEntity sellArea = null;
        List<HyundaiAttributeEntity> others = new ArrayList<>();

        for (String a : attributeArr) {
            if (StringUtils.isBlank(a)) {
                // special信息中一般会按照顺序排列数据，但却是存在对应数据为空时会为空字符串的情况
                continue;
            }

            String[] split = a.split(",", -1);
            // 做个校验，一旦数据变化了或不符合立刻解决
            if (split.length != 3) {
                throw new EPCDataNotStandardException("解析器【" + EPCAnalyserEnum.HYUNDAI + "】解析车型特殊描述不符合预期！");
            }
            String type = split[0];
            String code = split[1];
            String describe = split[2];
            HyundaiAttributeEntity attribute = HyundaiAttributeEntity.builder().type(type).code(code).describe(describe).build();
            switch (type) {
                case "DT":
                    drivePos = attribute;
                    break;
                case "WT":
                    sellArea = attribute;
                    break;
                default:
                    // 这里的数据最好按照数字的大小做排列，不过因为网站的返回本身就是有序的暂不处理也不考虑，后续有变动可以代码处理
                    others.add(attribute);
            }
        }
        return new MajorAttributeInfoDTO(drivePos, sellArea, others);
    }

    @AllArgsConstructor
    @FieldDefaults(level = AccessLevel.PRIVATE)
    private static class MajorAttributeInfoDTO {
        /**
         * 驾驶坐席
         */
        @Getter
        HyundaiAttributeEntity drivePos;
        /**
         * 销售地区
         */
        @Getter
        HyundaiAttributeEntity sellArea;
        /**
         * 其他说明信息
         */
        @Getter
        List<HyundaiAttributeEntity> others;
        /**
         * 返回左右肽编号
         */
        String getDrivePosCode() {
            return drivePos == null ? "" : drivePos.getCode();
        }
        /**
         * 返回销售地区编号
         */
        String getSellAreaCode() {
            return sellArea == null ? "" : sellArea.getCode();
        }
        /**
         * 返回当前属性组合字符串
         */
        String getCombination() {
            List<HyundaiAttributeEntity> attributes = getAttributes();
            return attributes.stream().map(a -> a == null ? "" : a.getCode()).collect(Collectors.joining("_"));
        }
        /**
         * 所有Attribute的Json文本
         */
        String getAttributeJson() {
            List<HyundaiAttributeEntity> attributes = getAttributesClearEmpty();
            return JSON.toJSONString(attributes);
        }
        /**
         * 返回所有Attribute属性
         */
        private List<HyundaiAttributeEntity> getAttributes() {
            List<HyundaiAttributeEntity> attributes = new ArrayList<>();
            attributes.add(drivePos);
            attributes.add(sellArea);
            if (CollectionUtils.isNotEmpty(others)) {
                attributes.addAll(others);
            }
            return attributes;
        }
        /**
         * 返回所有Attribute属性
         */
        private List<HyundaiAttributeEntity> getAttributesClearEmpty() {
            List<HyundaiAttributeEntity> attributes = getAttributes();
            attributes.removeIf(Objects::isNull);
            return attributes;
        }
    }

    @FieldDefaults(level = AccessLevel.PRIVATE)
    private static class HyundaiKiaAnalyticalResultDTO {
        /**
         * 对应 getVinList 接口返回数据
         */
        @Getter
        @Setter
        String vinList;
        /**
         * 对应 getVinCheck 接口返回数据
         */
        @Getter
        @Setter
        String vinCheck;
        /**
         * 对应 getVinInfo 接口返回数据，该接口的数据目前可通过切换语言区获得不同结果
         */
        @Getter
        @Setter
        String vinInfo;

        /**
         * 对应 getVinInfo 接口返回数据(英文)
         */
        @Getter
        @Setter
        String vinInfoEn;

        /**
         * 根据已知数据规则转化vinList
         * @return 转换后数据
         */
        String[] getVinListForPretreated() {
            return vinList.split(HyundaiModelDetailUtil.SECTION_STRING, -1)[0].split(HyundaiModelDetailUtil.FIELD_STRING, -1);
        }
        /**
         * 根据已知数据规则转化vinList
         * @return 转换后数据
         */
        String[] getVinCheckForPretreated() {
            return vinCheck.split(HyundaiModelDetailUtil.SECTION_STRING, -1)[0].split(HyundaiModelDetailUtil.FIELD_STRING, -1);
        }
        /**
         * 根据已知数据规则转化vinInfo
         * @return 转换后数据
         */
        String[][] getVinInfoForPretreated () {
            String[] vinInfoForSplit = vinInfo.split(HyundaiModelDetailUtil.SECTION_STRING, -1);
            String[][] result = new String[4][];
            if (vinInfoForSplit.length<3) {
                log.warn("hyundai在线解析数据可能发生更改,请人工排查");
                return result;
            }
            result[0] = vinInfoForSplit[0].split(HyundaiModelDetailUtil.FIELD_STRING, -1);
            result[1] = vinInfoForSplit[1].split(HyundaiModelDetailUtil.FIELD_STRING, -1);
            result[2] = vinInfoForSplit[2].split("(" + HyundaiModelDetailUtil.SENTENCE_STRING + "|" + HyundaiModelDetailUtil.FIELD_STRING + ")", -1);
            result[3] = getEnFeaterByVinInfoEn();
            return result;
        }

        private String[] getEnFeaterByVinInfoEn() {
            if (StringUtils.isBlank(vinInfoEn)) {
                return null;
            }
            String[] vinInfoForSplit = vinInfoEn.split(HyundaiModelDetailUtil.SECTION_STRING, -1);
            return vinInfoForSplit[2].split("(" + HyundaiModelDetailUtil.SENTENCE_STRING + "|" + HyundaiModelDetailUtil.FIELD_STRING + ")", -1);
        }
    }
}